/*!	 bone.cpp
**	 Bone File
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2008 Carlos López & Chirs Moore
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "bone.h"
#include "guid.h"
#include "valuenodes/valuenode_bone.h"
#include <ETL/stringf>
#include <algorithm>
#include <cmath>
#include <inttypes.h>

#endif

using namespace std;
using namespace etl;
using namespace synfig;

// Default constructor
Bone::Bone():
    origin_(Point(0, 0)),
    angle_(Angle::deg(0.0)),
    scalelx_(1.0),
    scalex_(1.0),
    length_(1.0),
    width_(0.1),
    tipwidth_(0.1),
    depth_(0.0),
    parent_(0)
{
    if (getenv("SYNFIG_DEBUG_NEW_BONES")) {
        printf("%s:%d new bone\n", __FILE__, __LINE__);
    }
}

// Constructor by origin and tip
Bone::Bone(const Point &o, const Point &t):
    origin_(o),
    angle_((t - o).angle()),
    scalelx_(1.0),
    scalex_(1.0),
    length_(1.0),
    width_(0.3),
    tipwidth_(0.3),
    depth_(0.0),
    parent_(0)
{
    if (getenv("SYNFIG_DEBUG_NEW_BONES")) {
        printf("%s:%d new bone\n", __FILE__, __LINE__);
    }
}

// Constructor by origin, angle, length, strength, parent bone (default = no parent)
Bone::Bone(const String &n, const Point &o, const Angle &a, const Real &l, ValueNode_Bone* p):
    name_(n),
    origin_(o),
    angle_(a),
    scalelx_(1.0),
    scalex_(1.0),
    length_(l),
    width_(0.3),
    tipwidth_(0.3),
    depth_(0.0),
    parent_(p)
{
    if (getenv("SYNFIG_DEBUG_NEW_BONES")) {
        printf("%s:%d new bone\n", __FILE__, __LINE__);
    }
}

const ValueNode_Bone*
Bone::get_parent()const
{
    return parent_;
}

void
Bone::set_parent(const ValueNode_Bone* parent)
{
    parent_ = parent;
}

// get_tip() member function
// @return The tip Point of the bone (calculated) based on
// tip=origin+[length,0]*Scalex(scalex*scalelx,0)*Rotate(alpha)
Point
Bone::get_tip()
{
    Matrix s, r, sr;
    s.set_scale(scalex_ * scalelx_, 0);
    r.set_rotate(angle_);
    sr = s * r;
    return (Point)sr.get_transformed(Vector(length_, 0));
}

// Get the string of the Bone
// @return String type. A string representation of the bone
// components.
synfig::String
Bone::get_string()const
{
    return strprintf("N=%s O=(%.4f %.4f) a=%.4f slx=%.4f sx=%.4f l=%.4f w=%.4f tw=%.4f or=%.4f P=%lx",
                     name_.c_str(),
                     origin_[0], origin_[1],
                     Angle::deg(angle_).get(),
                     scalelx_, scalex_, length_, width_, tipwidth_, depth_, uintptr_t(parent_));
}

bool
Bone::is_root()const
{
    return get_parent()->is_root();
}

Bone::Shape
Bone::get_shape() const
{
    Matrix matrix = get_animated_matrix();
    Vector origin = matrix.get_transformed(Vector(0.0, 0.0));
    Vector direction = matrix.get_transformed(Vector(1.0, 0.0), false).norm();
    Real length = get_length() * get_scalelx();

    if (length < 0) {
        length *= -1;
        direction *= -1;
    }

    Shape shape;
    shape.p0 = origin;
    shape.p1 = origin + direction * length;

    shape.r0 = fabs(get_width());
    shape.r1 = fabs(get_tipwidth());

    return shape;
}

Real
Bone::distance_to_shape_center_percent(const Shape &shape, const Vector &x)
{
    static const Real precision = 0.000000001;

    const Vector &p0 = shape.p0;
    const Vector &p1 = shape.p1;
    Real r0 = fabs(shape.r0);
    Real r1 = fabs(shape.r1);

    Real length = (p1 - p0).mag();

    Real percent_p0 = r0 > precision ? 1.0 - (x - p0).mag() / r0 : 0.0;
    Real percent_p1 = r1 > precision ? 1.0 - (x - p1).mag() / r1 : 0.0;

    // check line
    Real percent_line = 0.0;

    if (length + precision > fabs(r1 - r0)) {
        Real cos0 = (r0 - r1) / length;
        Real cos1 = -cos0;

        Real sin0 = sqrt(1 + precision - cos0 * cos0);
        Real sin1 = sin0;

        Real ll = length - r0 * cos0 - r1 * cos1;
        Vector direction = (p1 - p0) / length;
        Vector pp0(p0 + direction * (r0 * cos0));
        Vector pp1(p0 + direction * (length - r1 * cos1));
        Real rr0 = r0 * sin0;
        Real rr1 = r1 * sin1;

        Real pos_at_line = (x - pp0) * direction / ll;

        if (pos_at_line > 0.0 && pos_at_line < 1.0) {
            Real distance = fabs((x - pp0) * direction.perp());
            Real max_distance = rr0 * (1.0 - pos_at_line) + rr1 * pos_at_line;

            if (max_distance > 0.0) {
                percent_line = 1.0 - distance / max_distance;
            }
        }
    }

    Real percent = 0.0;

    if (percent_p0 > percent) {
        percent = percent_p0;
    }

    if (percent_p1 > percent) {
        percent = percent_p1;
    }

    if (percent_line > percent) {
        percent = percent_line;
    }

    return percent;
}

Real
Bone::influence_function(Real x)
{
    return sin(x * PI / 2.0);
}